<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <title></title>
</head>

<body>
  <script>
    {

    }

    {

    }
    {

    }

    {

    }
    {

    }

    {

    }
    {

    }

    {

    }
    {
      /* 
      canvas 可以通过 toDataURL() 或 toBlob() 方法导出不同格式的图片,支持的图片格式包括:
      - PNG: 通过 toDataURL('image/png') 或 toBlob('image/png') 导出。
      - JPG/JPEG: 通过 toDataURL('image/jpeg') 或 toBlob('image/jpeg') 导出。
      - WEBP: 通过 toDataURL('image/webp') 或 toBlob('image/webp') 导出。浏览器兼容性较差。
      - SVG: 通过 toDataURL('image/svg+xml') 导出 SVG 格式图片。
      - BMP: 通过 toDataURL('image/bmp') 或 toBlob('image/bmp') 导出。浏览器支持不太好。
      除此之外,canvas 还支持导出 PDF 格式文档,可以通过下面的 js 库实现:
      - pdfMake:是一个纯 js 的 PDF 生成库,可以用于 canvas 导出 pdf。
      - jspdf:是一个 js 的 PDF 文档处理库,可以把 canvas 转为图像嵌入生成 pdf。
      所以在兼容性方面,PNG 和 JPEG 是 canvas 导出图片最推荐的格式。如果要导出vector图形,可以使用 SVG 格式。如果需要打印文档质量,可以考虑导出 PDF。 （已编辑
      */
    }

    {
      // 整理到web worker的语雀里面去  写一个es5版本的导出，试试大图片是不是真的卡(canvas 并排绘制节点)
      /* 
      主要步骤是:
1. 主线程中获取 canvas 对象,传给 Worker 线程
2. Worker 线程中接收到 canvas 对象后,使用 toBlob 转换为 Blob
3. Worker 通过 createObjectURL 生成 png 文件的 URL
4. Worker 将生成的 png URL 传回主线程
5. 主线程中接收到 png URL,然后创建 a 标签下载
这样就可以将 canvas 导出 png 图片的操作放到 Worker 线程中运行,避免阻塞主线程。 
      */
      // 主线程
      const canvas = document.getElementById('myCanvas');
      const worker = new Worker('export.js');
      worker.postMessage(canvas);
      worker.onmessage = function (event) {
        const pngUrl = event.data;
        const link = document.createElement('a');
        link.href = pngUrl;
        link.download = 'myCanvas.png';
        link.click();
        URL.revokeObjectURL(pngUrl);
      }

      // Worker 线程 (export.js)
      self.onmessage = function (event) {
        const canvas = event.data;
        canvas.toBlob(function (blob) {
          const pngUrl = URL.createObjectURL(blob);
          self.postMessage(pngUrl);
        }, 'image/png');
      }
    }
    {
      //  整理到js函数库？  导出的格式还有哪些？
      // 在canvas上绘制完成后
      const canvas = document.getElementById('myCanvas');

      // 将canvas转成blob
      canvas.toBlob(function (blob) {

        // 生成一个URL
        const pngUrl = URL.createObjectURL(blob);

        // 创建a标签进行下载
        const link = document.createElement('a');
        link.href = pngUrl;
        link.download = 'myCanvas.png';

        link.click();

        // 释放URL对象
        URL.revokeObjectURL(pngUrl);

      }, 'image/png');

    }
    {
      const obj = { name: "iceCode" };
      const newObj = Object.defineProperties(obj, {
        //对于已经有的属性，会修改当前值
        name: {
          value: "icedCode",
          writable: true,
          enumerable: true,
        },
        //对于没有的会新增
        age: {
          value: 18,
          writable: true,
          enumerable: true,
        },
      });
      console.log(newObj);//{name: 'icedCode', age: 18}
    }
    {
      async function checkCamera() {
        const navigator = window.navigator.mediaDevices;
        // const devices = await navigator.getUserMedia({video: true});
        const devices = await navigator.enumerateDevices();
        console.log(devices)
        devices.forEach(device => {
          console.log(device.kind);// audioinput:音频 videoinput:视频
          console.log(device.label)
          console.log(device.deviceId)
          console.log(device.groupId)
        });
      }
    }

    {
      // 将Vue组件转换为Web components的示例
      import { createApp } from 'vue'
      const app = createApp({
        // Vue组件的选项
      })
      app.mount('#app')

      // 转换为Web components
      const MyComponent = app.component('my-component')

      if (window.customElements && window.customElements.define) {
        window.customElements.define('my-component', MyComponent)
      }
    }
    {
      // vue3中使用components
      import { createApp } from 'vue'
      import VueCustomElement from '@vue/web-component-wrapper'

      import MyWebComponent from './MyWebComponent.vue'

      const app = createApp({})
      app.component('my-web-component', MyWebComponent)

      // 使用VueCustomElement插件
      app.use(VueCustomElement)

      app.mount('#app')

      // 注册为Web component
      app.component('my-web-component').then((component) => {
        VueCustomElement(component)
      })
    }
    {
      // https://developer.mozilla.org/zh-CN/docs/Web/API/Web_components
      // https://omi.cdn-go.cn/home/latest/
      // https://developer.aliyun.com/article/1129999
      // https://dashen.wang/3790.html
      // https://zhuanlan.zhihu.com/p/618025277
      // https://blog.csdn.net/xgangzai/article/details/128710810
      class MyElement extends HTMLElement {
        constructor() {
          super();
          const shadowRoot = this.attachShadow({ mode: 'open' });
          const div = document.createElement('div');
          div.textContent = 'Hello,boxue!';
          shadowRoot.appendChild(div);
        }
      }
      customElements.define('my-element', MyElement);
      // Constructable Stylesheets
    }
    {
      // 在这个例子中，我们创建了一个样式表 sheet，然后在 my-element 组件的 Shadow DOM 中应用了这个样式表。无论我们创建了多少个 my-element 组件，样式表的代码都只需要加载一次。
      const sheet = new CSSStyleSheet();
      sheet.replaceSync('p{color:red;}');
      customElements.define('my-element', class extends HTMLElement {
        constructor() {
          super();
          this.attachShadow({ mode: 'open' });
        }
        connectedCallback() {
          this.shadowRoot.adoptedStyleSheets = [sheet];
          this.shadowRoot.innerHTML = '<p>boxue is 666;</p>';
        }
      });
    }
    {
      // 让组件接受宽度作为属性,根据传入的宽度变更样式。
      class MyComponent extends HTMLElement {
        static get observedAttributes() {
          return ['width'];
        }

        attributeChangedCallback(name, oldValue, newValue) {
          // 根据 width 修改样式
        }
      }

      // 其它：接收父容器宽度Context  组件订阅 context 来获取父级容器宽度并自适应。
    }
    {
      // js map和weakmap
      // Map 和 WeakMap 是 JavaScript 中两种类似哈希表的集合数据类型。
      /* 
      Map 和 WeakMap 是 JavaScript 中两种类似哈希表的集合数据类型。
它们的主要区别有:
1. Map 的键可以是任意值,包括函数、对象或基本类型。WeakMap 的键只能是对象类型。
2. Map 的键值对会一直存在,直到手动删除。WeakMap 的键值对是弱引用,如果没有其他引用指向键名对象,会被垃圾回收机制自动删除。
3. Map 是可迭代的。WeakMap 是不可迭代的。
4. Map 可以直接获取大小。WeakMap 没有 size 属性。
简单来说:
- Map:键值对会一直存在,键可以是任意类型,可迭代的。
- WeakMap:键值对可以被垃圾回收,键只能是对象,不可迭代的。
常见的使用场景包括:
- Map 用于存储需要快速查找的数据,类似对象;
- WeakMap 用于存储私有数据,避免内存泄漏。
所以基本上 Map 的使用更加广泛,而 WeakMap 使用在一些特定的防止内存泄漏的场景。
      */
    }
    {
      // 下载一点文章到桌面也行，能否用nodejs批量下载？  ctrl+s保存的时候，注意没有网页下半部分没有加载出来的图片资源
      // https://github.com/iamkun/dayjs 整理下api
      // nuxt的学习
      // 如果使用nuxt.js 做登录页的秒开
      // 模拟下视频流的传输和播放，用nodejs做后台， 要不要试试ts的方式？
      // 模拟下单点登录
      // webgpu的学习，看下一灯的视频，看下怎么落地的实际项目
      // canvas的各种api
      // pathon的学习计划
      // node的学习计划
      // bff的学习计划
      // angluar的学习计划
      // java的学习计划
      // mysql的学习计划
      // wasm的学习计划
      // go的学习计划
      // gzip或br 在node上试试 
      // 前端安全之跨站脚本攻击和请求伪造
    }
    {
      // 请求失败时无感刷新token
    }
    {
      // 如何压缩前端项目中 JS 的体积
      /* 
      gzip 或者 brotli 压缩，在网关处(nginx)开启
      使用 webpack-bundle-analyzer 分析打包体积，替换占用较大体积的库，如 moment -> dayjs
      使用支持 Tree-Shaking 的库，对无引用的库或函数进行删除，如 lodash -> lodash/es
      对无法 Tree Shaking 的库，进行按需引入模块，如使用 import Button from 'antd/lib/Button'，此处可手写 babel-plugin 自动完成，但不推荐
      使用 babel (css 为 postcss) 时采用 browserlist，越先进的浏览器所需要的 polyfill 越少，体积更小
      code spliting，路由懒加载，只加载当前路由的包，按需加载其余的 chunk，首页 JS 体积变小 (PS: 次条不减小总体积，但减小首页体积)
      使用 webpack 的 splitChunksPlugin，把运行时、被引用多次的库进行分包，在分包时要注意避免某一个库被多次引用多次打包。此时分为多个 chunk，虽不能把总体积变小，但可提高加载性能 (PS: 此条不减小总体积，但可提升加载性能)
      */
    }
    {
      // ref创建的effect只执行了一次
      /* 
      在Vue 3中，使用ref创建的响应式数据在effect中只会触发一次。这是因为ref创建的数据是单值的，不会自动追踪其内部的变化。
      
      如果你想要在effect中监听ref数据的变化，你可以使用toRefs函数将其转换为一个响应式对象，然后使用watch函数来监听变化。
      */
      import { ref, reactive, toRefs, watch } from 'vue';

      const count = ref(0);
      const reactiveData = reactive({ foo: 'bar' });

      watch([count, toRefs(reactiveData)], ([countValue, { foo }]) => {
        console.log('count has changed:', countValue);
        console.log('foo has changed:', foo);
      });

    }
    { }
    { }
    {
      let outer = function () {
        let name = 'Jake';
        return function () {
          return name;
        };
      };
      /* 
      这会导致分配给 name 的内存被泄漏。以上代码创建了一个内部
      闭包，只要 outer 函数存在就不能清理 name ，因为闭包一直
      在引用着它。假如 name 的内容很大（不止是一个小字符串），
      那可能就是个大问题了
      */
    }
    {
      console.log(Math.pow(16, 0.5)); // 4
      console.log(16 ** 0.5); // 4

      let squared = 3;
      squared **= 2;
      console.log(squared); // 9

      let sqrt = 16;
      sqrt **= 0.5;
      console.log(sqrt); // 4
    }
    {
      function Foo() { }
      let f = new Foo();
      console.log(Foo[Symbol.hasInstance](f)); //
      true
      class Bar { }
      let b = new Bar();
      console.log(Bar[Symbol.hasInstance](b)); //
      true
    }
    {
      class Bar { }
      class Baz extends Bar {
        static [Symbol.hasInstance]() {
          return false;
        }
      }
      let b = new Baz();
      console.log(Bar[Symbol.hasInstance](b)); // true
      console.log(b instanceof Bar); // true
      console.log(Baz[Symbol.hasInstance](b)); // false
      console.log(b instanceof Baz); // false
    }
    {
      class Foo {
        async *[Symbol.asyncIterator]() { }
      }
      let f = new Foo();
      console.log(f[Symbol.asyncIterator]());
      // AsyncGenerator {<suspended>}
      /* 
      技术上，这个由 Symbol.asyncIterator 函数生成的对象应
该通过其 next() 方法陆续返回 Promise 实例。可以通过显式
地调用 next() 方法返回，也可以隐式地通过异步生成器函数返
回：
      */
    }
    {
      class Emitter {
        constructor(max) {
          this.max = max;
          this.asyncIdx = 0;
        }
        async *[Symbol.asyncIterator]() {
          while (this.asyncIdx < this.max) {
            yield new Promise((resolve) =>
              resolve(this.asyncIdx++));
          }
        }
      }
      async function asyncCount() {
        let emitter = new Emitter(5);
        for await (const x of emitter) {
          console.log(x);
        }
      }
      asyncCount();
      // 0
      // 1
      // 2
      // 3
      // 4
    }
    {
      let s1 = Symbol('foo'),
        s2 = Symbol('bar'),
        s3 = Symbol('baz'),
        s4 = Symbol('qux');
      let o = {
        [s1]: 'foo val'
      };
      // 这样也可以：o[s1] = 'foo val';
      console.log(o);
      // {Symbol(foo): foo val}
      Object.defineProperty(o, s2, {
        value: 'bar val'
      });
      console.log(o);
      // {Symbol(foo): foo val, Symbol(bar): bar
      Object.defineProperties(o, {
        [s3]: { value: 'baz val' },
        [s4]: { value: 'qux val' }
      });
      console.log(o);
    }
    {
      /* 动态组件的模拟
      <component v-for="(item,index) in componentList" :key="index" :is="item"></component>
      import ColorIn from '@/components/Magic/ColorIn.vue'
import LineIn from "@/components/Magic/LineIn.vue";
import Header from "@/components/Magic/Header.vue";
import Footer from "@/components/Magic/Footer.vue";

export default{
      components:{
        ColorIn,
        LineIn,
        Header,
        Footer
    }
}
      */
    }
    {
      // 下面的代码会导致栈溢出码？
      function foo() {
        setTimeout(f00, 0)
      }
      foo();
      /* 
      不，下面的代码不会导致栈溢出。
      在这段代码中，foo函数通过setTimeout将f00函数推入事件队列，并在下一个事件循环中执行。通过将f00函数推入事件队列，foo函数将立即返回，不会导致堆栈溢出错误。
      使用setTimeout将函数推入事件队列是一种异步执行的方式，它不会阻塞主线程，因此不会导致堆栈溢出。相反，它允许主线程继续执行其他任务，然后在适当的时候执行被推入事件队列的函数。
      */
    }
    {
      function retry(promiseFn, retries) {
        return new Promise((resolve, reject) => {
          const attempt = (count) => {
            promiseFn()
              .then(resolve)
              .catch((error) => {
                if (count < retries) {
                  console.log(`Retrying... (${count + 1}/${retries})`);
                  attempt(count + 1);
                } else {
                  reject(error);
                }
              });
          };

          attempt(0);
        });
      }
      // 一个可能会失败的异步操作的函数
      function asyncOperation() {
        return new Promise((resolve, reject) => {
          // 在这里进行异步操作
          // 如果操作成功，调用 resolve(result)
          // 如果操作失败，调用 reject(error)
        });
      }

      // 使用 retry 函数进行重试
      retry(asyncOperation, 3)
        .then((result) => {
          // 操作成功
          console.log(result);
        })
        .catch((error) => {
          // 重试多次后仍然失败
          console.error(error);
        });
    }

    {
      (function ($) {
        $.enterfocus = function (selector, callback) {
          var boxArray = [].slice.call(document.querySelectorAll(selector));
          for (var index in boxArray) {
            var box = boxArray[index];
            box.addEventListener('keyup', function (event) {
              if (event.keyCode == 13) {
                var boxIndex = boxArray.indexOf(this);
                if (boxIndex == boxArray.length - 1) {
                  if (callback) callback();
                } else {
                  //console.log(boxIndex);
                  var nextBox = boxArray[++boxIndex];
                  nextBox.focus();
                }
              }
            }, false);
          }
        };
      }(window.mui = window.mui || {}));
      $.enterfocus('#login-form input', function () {
        $.trigger(loginButton, 'tap');
      });
    }
    {
      /* 
        下拉刷新
        上拉加载
 
        日期的控件
        选择人的控件
 
        https://dev.dcloud.net.cn/mui/ui/ 预渲染子页面   prerender-spa-plugin
      */
    }

    {
      // 模拟微任务 不行了看渡一的
    }

    {
      // chrome://inspect/#apps 是干嘛的？
    }
    {
      // null和undefined的区别
      // null表示一个对象被定义了，但值是null
      // undefined表示一个值被定义了，但值是undefined
      // 判断一个变量是否是undefined
      // var a;
      // console.log(a === undefined);
      // console.log(a === null);
      // 判断一个变量是否是null
      // var a = null;
      // console.log(a === undefined);
      // console.log(a === null);

    }

    {
      import { useLocalStorage } from 'vueuse';
      const myValue = useLocalStorage('myKey', 'defaultValue');

    }
    {
      // https://mp.weixin.qq.com/s/mROjtpoXarN--UDfEMqwhQ GoDB.js
    }

    {
      // 居中方式 display:grid;place-item:center center;

      // 竖排文字
      // writing-mode: tb-lr;
      // transform:rotate(180deg);
    }
    {
      // https://cssgr.id/
      // https://csslayout.io/
      // https://loading.io/flexbox
      // https://cssgrid-generator.netlify.app/
      // https://grid.layoutit.com/
    }

    {
      // https://rxviz.com/examples/today-is
    }
    {
      // 使用数组实现栈的结构
      function Stack() {
        let items = [];
        this.size = function () {
          return items.length
        }
        this.push = function (item) {
          items.push(item)
        }
        this.pop = function () {
          let item = items.pop()
          console.log('pop', item)
          return item
        }
        this.top = function () {
          let length = items.length;
          console.log('top', items[length - 1])
        }
        this.show = function () {
          console.log(items)
        }
        this.clear = function () {
          items.length = 0
        }
        this.isEmpty = function () {
          console.log(items.length === 0)
          return items.length === 0
        }
      }


      let newStack = new Stack();
      newStack.push(12);
      newStack.show()
      newStack.top()
      newStack.pop();
      newStack.isEmpty();
      newStack.show()
      newStack.push(1);
      newStack.push(5);
      newStack.isEmpty();
      newStack.top()
      newStack.show()
      newStack.clear();
      newStack.show()
    }

    {
      // axios中取消重复请求，利用new Map? 封装一下request，并且是否应该有一个请求参数表示当前请求是否要经过过滤
    }
    {
      // https://mp.weixin.qq.com/s/9636aZRSSsx7_8tIa_XjcQ
      // diff算法方面
      // 编译上的优化
      // 说说浏览器渲染页面的过程
      // 手写promise
      // 手写快速排序
    }

    {
      // Object.create(null) 该空对象没有继承 Object.prototype 对象 ????百度下，这样做的好处
    }

    {
      (() => {
        function block() {
          if (window.outerHeight - window.innerHeight > 200 || window.outerWidth - window.innerWidth > 200) {
            document.body.innerHTML = "检测到非法调试,请关闭后刷新重试!";
          }
          setInterval(() => {
            (function () {
              return false;
            }
            ['constructor']('debugger')
            ['call']());
          }, 50);
        }
        try {
          block();
        } catch (err) { }
      })();
    }

    {
/* 
https://mp.weixin.qq.com/s/p9ZajbkpWJ88LKXCLkA1oQ

将下面的 JSON 添加到网页中，可以触发浏览器对 next.html 和 next17.html 的数据预取
Prefetch 的推测规则只会对 HTML 文档进行预取，而不会预取页面上的子资源。
与旧的 <link rel="prefetch"> （预取的数据存放在 HTTP 缓存）机制不同，通过 Speculation Rules 进行的预取，数据是保存在内存中的，所以浏览器一旦需要可以更快的访问到这些资源。

数据预取:
<script type="speculationrules">
  {
    "prefetch": [
      {
        "source": "list",
        "urls": ["next.html", "next17.html"]
      }
    ]
  }
  </script>

  */
  }

  {
  /*
  预渲染
  <script type="speculationrules">
  {
    "prerender": [
      {
        "source": "list",
        "urls": ["next.html", "next17.html"]
      }
    ]
  }
</script>
  目前浏览器只能支持同站站点的预渲染，例如 https://1.conardli.com 可以预 https://17.conardli.com 上的页面。注意如果是非同源的情况，需要预渲染的页面必须包括一个
  Supports-Loading-Mode: credentialed-prerender Header 。

  但是，与 Prefetch 不同的是，预渲染的请求在 Network 看板里是没办法直接看到的，因为它们是在 Chrome 中的单独渲染进程中获取和渲染的。

  这时候，我们只能通过 Application 的 Preloading 看板来进行调试了：


  */
  }

  {
  // js crypto看下codeium
  // chrome://predictors Chrome 对你页面中 URL 的预测
  }

  {
  /*
  常见微任务：queueMicrotask、Promise、MutationObserve等。
  常见宏任务：ajax、setTimeout、setInterval、script（js整体代码）、IO操作、UI交互、postMessage等。
  */
  }

  {
  // https://convertio.co/zh/
  Convertio是一个用户友好的在线文件转换工具。它支持超过309种不同的文档、图像、电子表格、电子书、存档、演示文稿、音频和视频格式。通过Convertio，您可以轻松地转换文件类型，如PNG到JPEG，SVG到PNG，PNG到ICO等等。
  }

  {
  /*
  const arr = ["￥7", 500, 8123, 12];
  const newArr = arr.toLocaleString("ja-JP", { style: "currency", currency: "JPY" });
  console.log(newArr);//￥7,￥500,￥8,123,￥12
  //如果不传参数，则效果于toString一样
  const newArr = arr.toLocaleString();
  console.log(newArr);//￥7,500,8,123,12
  */
  }
  // Array.fromAsync()
  {
  /*
  const arr1 = ["March", "Jan", 6, 2, "A", "a"];
  const newArr = arr1.flatMap((item, index) => {
  return [item, index];
  });
  //可以看出本应该是双层数组的，却被扁平化了
  console.log(newArr);//['March', 0,'Jan', 1,6,2,2,3,'A',4,'a', 5]

  const newArr = arr1.flatMap((item, index) => {
  return [[item, index]];
  });
  //仅只能扁平化一层
  console.log(newArr);//[[ 'March', 0 ],[ 'Jan', 1 ], [ 6, 2 ], [ 2, 3 ],[ 'A', 4 ],[ 'a', 5 ]]
  */
  }

  {
  /*
  const arr1 = ["March", "Jan", 6, 2, "A", "a"];
  //从索引为三个位置开始复制，到索引为5的位置，但不包含5，从索引为1的位置粘贴并覆盖
  const newArr = arr1.copyWithin(1, 3, 5);
  console.log(newArr);//[ 'March', 2, 'A', 2, 'A', 'a' ]

  //为负数时从后往前数-2从A的位置到-1不包括-1的位置，也就是将A赋值并覆盖到了索引为0的位置
  const newArr = arr1.copyWithin(0, -2, -1);
  console.log(newArr);//[ 'A', 'Jan', 6, 2, 'A', 'a' ]

  //这种结束索引位置在开始索引位置之前的都不生效
  const newArr = arr1.copyWithin(0, -2, 2);
  const newArr = arr1.copyWithin(0, -2, -4);
  console.log(newArr);//[ 'March', 'Jan', 6, 2, 'A', 'a' ]
  */
  }

  {
  console.log(2 && 4);// 4
  console.log(2 || 4);// 2

  console.log(1 < 2 && 4> 5);// false
    console.log(1 < 2 && 6> 5);// true

      console.log(1 < 2 || 4> 5);// true
        console.log(1 < 0 || 4> 5);// false
          }

          {
          // <Profiler> 用于编程式测量 React 树的渲染性能。 react
            // 当调用 onRender 回调函数时，React 会告诉你相关信息。
            // 它可以帮助我们定位哪些组件渲染较慢，以及哪些组件触发了不必要的渲染。
            // 进行性能分析会增加一些额外的开销，因此在默认情况下，它在生产环境中是被禁用的。
            /*
            <Profiler id="App" onRender={onRender}>
              <App />
            </Profiler>
            function onRender(id, phase, actualDuration, baseDuration, startTime, commitTime) {
            // 对渲染时间进行汇总或记录...
            }
            */
            }

            {
            var observer = new MutationObserver(function (mutations) {
            mutations.forEach(function (mutation) {
            console.log('页面有变化');
            });
            });

            var config = { attributes: true, childList: true, characterData: true, subtree: true };
            observer.observe(document.body, config);

            setTimeout(function () {
            observer.disconnect();
            console.log('页面可能白屏');
            }, 5000);
            }

            {
            // lodash、underscore或immutable.js，它们提供了更全面和可靠的拷贝功能。
            // 看看这些库的使用
            }

            {
            function print32Bit(num) {
            let binary = (num >>> 0).toString(2).padStart(32, '0');
            console.log(binary);
            }

            let num = 42;
            print32Bit(num);
            /*
            在JavaScript中，位运算符>>>是无符号右移运算符。它将操作数的二进制表示向右移动指定的位数，并用零填充左侧的空位。
            (num >>> 0)的作用是将num转换为32位无符号整数。通过使用>>>
            0，我们将num的二进制表示向右移动0位，即不进行实际的移位操作。但这样做可以确保num被视为无符号整数，并且在进行位运算时不会受到符号位的影响。
            请注意，JavaScript中的数值默认是使用64位双精度浮点数来表示的，而不是32位整数。使用>>> 0的技巧可以将数值强制转换为32位无符号整数，以便进行位运算或获得32位二进制表示。
            */
            }

            {
            // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Unsigned_right_shift
            let num = 300;
            console.log(num >>> 0);// 300
            console.log(0.001 >>> 0);// 0

            console.log(num >>> 2);// 75
            console.log(0.001 >>> 2);// 0

            console.log((num >>> 0).toString(2));//100101100 tostring(2)也是一个知识点

            }

            {
            //暂无数据的函数
            function noDataAvailableFn(obj) {
            var thisHeight = (obj && obj.height) || 100;
            var id = (obj && obj.id) || '';
            var bgc = (obj && obj.bgc && ("background-color: " + obj.bgc + ";")) || '';
            return '<div id="' + id + '"
              style="height:' + thisHeight + 'px;position:relative;border-radius:4px;' + bgc + '"> <img
                src="' + $noDataImgBase64 + '"
                style="display:inline-block;position: absolute;left:50%;top:50%;width: 85px;height: 85px;transform: translate(-50%,-50%);" />
            </div> ';
            }

            // 暂无数据
            ''
            }
            {

            }
            {

            }
            {
            // 前端埋点 错误监控 sentry
            }
            {
            // window.parent和window.opener的区别 codegeex
            }
            {
            /* .parent {
            display: grid;
            place - items: center;
            } */
            }
            {
            // JS继承有哪几种方式？ https://www.bilibili.com/video/BV11k4y1F7jW
            }
            {
            // 零宽字符 codegeex
            }
            {
            /* html中&ensp;也是空格？
            是的，在HTML中，&ensp;（即&nbsp;）是一个空格字符，用于替代浏览器默认的空格显示。虽然它的显示可能与空格相同，但它实际上是一个独立的字符，具有特定的含义和用途。例如，在CSS中，您可以使用white-space:
            nowrap来确保在文本中不会换行，从而避免出现多余的空格。
            */
            }
            {
            if (value !== null && value !== undefined && value !== '') { }
            // 等价于 ==>
            if ((value ?? '') !== '') { }
            }
            {
            // 下面的代码是一个简单的实现，尽管它不太健壮。 纯函数总能够根据输入来做缓存
            var memoize = function (f) {
            var cache = {};

            return function () {
            var arg_str = JSON.stringify(arguments);
            cache[arg_str] = cache[arg_str] || f.apply(f, arguments);
            return cache[arg_str];
            };
            };
            }
            {
            // Web Components的定义和使用，给出示例
            class MyButton extends HTMLElement {
            constructor() {
            super();

            const shadowRoot = this.attachShadow({ mode: 'open' });

            const button = document.createElement('button');
            button.textContent = this.getAttribute('text') || 'Click me';

            button.addEventListener('click', () => {
            const onClick = this.getAttribute('onClick');
            if (onClick) {
            // 调用onClick属性指定的点击事件处理程序
            eval(onClick);
            }
            });

            shadowRoot.appendChild(button);
            }
            }
            customElements.define('my-button', MyButton);


            // Web Components自定义结构
            /*
            Web Components 允许您自定义元素的结构，以便更好地满足您的需求。以下是一些方法可以自定义Web组件的结构：

            使用Shadow DOM：Shadow DOM 允许您在自定义元素的内部创建一个封装的 DOM 子树。通过使用 Shadow DOM，您可以将样式和结构封装在组件内部，防止组件的样式和结构受到外部影响。您可以使用
            attachShadow() 方法创建 Shadow DOM，并使用 innerHTML 或 appendChild() 方法向 Shadow DOM 添加子元素。

            使用插槽（Slots）：插槽允许您在自定义元素的模板中定义可替换的内容区域。通过在自定义元素的模板中使用 <slot> 元素，您可以在使用组件时向其中插入内容。使用 <slot> 元素的 name
                属性可以创建具有不同插槽名称的多个插槽。

                使用模板（Templates）：模板允许您定义可重用的 HTML 片段，以便稍后克隆和插入到文档中。您可以使用 <template> 元素来定义模板，并使用 content 属性获取模板的内容。通过使用
                  importNode() 方法或 <template> 元素的 content.cloneNode() 方法，您可以将模板克隆为实际的 DOM 元素，并将其插入到组件中。

                    下面是一个示例，展示了如何使用 Shadow DOM 和插槽来自定义一个简单的 Web 组件的结构：
                    */
                    class MyCustomComponent extends HTMLElement {
                    constructor() {
                    super();
                    const shadowRoot = this.attachShadow({ mode: 'open' });

                    shadowRoot.innerHTML = `
                    <style>
                      /* 在 Shadow DOM 中定义样式，不会受到外部样式的影响 */
                      :host {
                        display: block;
                        border: 1px solid black;
                        padding: 16px;
                      }
                    </style>

                    <slot name="title"></slot>
                    <slot name="content"></slot>
                    `;
                    }
                    }
                    customElements.define('my-custom-component', MyCustomComponent);



                    class MyCustomComponent extends HTMLElement {
                    constructor() {
                    super();
                    const shadowRoot = this.attachShadow({ mode: 'open' });

                    const button = document.createElement('button');
                    button.textContent = 'Click me';

                    // 添加点击事件监听器
                    button.addEventListener('click', () => {
                    // 在点击时触发自定义事件
                    const event = new CustomEvent('myEvent', {
                    detail: {
                    message: 'Button clicked!'
                    }
                    });
                    this.dispatchEvent(event);
                    });

                    shadowRoot.appendChild(button);
                    }
                    }
                    customElements.define('my-custom-component', MyCustomComponent);

                    // 在页面中监听自定义事件
                    const myComponent = document.querySelector('my-custom-component');
                    myComponent.addEventListener('myEvent', (event) => {
                    console.log(event.detail.message);
                    });


                    class MyCustomComponent extends HTMLElement {
                    constructor() {
                    super();
                    const shadowRoot = this.attachShadow({ mode: 'open' });

                    // 创建一个包含 layui 组件的 HTML 结构
                    const div = document.createElement('div');
                    div.classList.add('layui-form-item');

                    const label = document.createElement('label');
                    label.classList.add('layui-form-label');
                    label.textContent = 'Username';

                    const inputDiv = document.createElement('div');
                    inputDiv.classList.add('layui-input-block');

                    const input = document.createElement('input');
                    input.classList.add('layui-input');
                    input.setAttribute('type', 'text');
                    input.setAttribute('placeholder', 'Enter your username');

                    inputDiv.appendChild(input);
                    div.appendChild(label);
                    div.appendChild(inputDiv);

                    shadowRoot.appendChild(div);
                    }
                    }
                    customElements.define('my-custom-component', MyCustomComponent);
                    }
                    {

                    }
                    {

                    }
                    {
                    // 开发一款cli
                    }
                    {
                    /*
                    new Map() 创建的 Map 对象会被 JavaScript 的垃圾回收机制正确地进行处理。

                    在 JavaScript 中，垃圾回收是自动进行的，它会监测不再被引用的对象，并将它们标记为可回收的。一旦对象不再被引用，垃圾回收机制就会释放它们所占用的内存空间，以便可以重新利用。

                    对于 new Map() 创建的 Map 对象来说，当没有任何引用指向该对象时，垃圾回收机制会将它标记为可回收的。这意味着，只要没有其他代码引用该 Map
                    对象，它就会被垃圾回收机制自动清理并释放内存。

                    要确保 Map 对象能够被垃圾回收，需要注意在不再需要使用 Map 对象时，将其引用置为 null 或让其超出作用域。这样，垃圾回收机制就能够正确地处理该对象，并释放它所占用的内存空间。

                    总而言之，new Map() 创建的 Map 对象会被 JavaScript 的垃圾回收机制妥善处理。只要没有任何引用指向该对象，它将被自动清理并释放内存。
                    */
                    }
                    {
                    // 创建一个新的 Map 对象
                    const myMap = new Map();

                    // 向 Map 中添加键值对
                    myMap.set('key1', 'value1');
                    myMap.set('key2', 'value2');
                    myMap.set('key3', 'value3');

                    // 获取 Map 中的值
                    console.log(myMap.get('key1')); // 输出: value1

                    // 检查 Map 中是否存在某个键
                    console.log(myMap.has('key2')); // 输出: true

                    // 获取 Map 的大小（键值对的数量）
                    console.log(myMap.size); // 输出: 3

                    // 遍历 Map 中的所有键值对
                    myMap.forEach((value, key) => {
                    console.log(`${key} => ${value}`);
                    });

                    // 删除 Map 中的一个键值对
                    myMap.delete('key3');

                    // 清空 Map 中的所有键值对
                    myMap.clear();
                    }
                    {
                    // vite是如何利用Esbuild来提升性能的？
                    /*
                    vite 使用 esbuild 来提升性能的方式是通过利用 esbuild 的快速打包和转译能力。vite 在开发过程中使用 esbuild 来实时地将源代码转译为浏览器可执行的 JavaScript
                    代码。这种实时转译的方式避免了传统的打包过程，因此能够加快开发过程中的重新加载速度。

                    vite 利用 esbuild 的快速打包能力，可以在开发过程中非常快速地重新生成和更新模块。esbuild
                    非常高效，因为它采用了并行和增量的构建方式，只重新构建发生变化的模块，而不是重新构建整个项目。这样可以显著减少开发过程中的重新构建时间。

                    此外，vite 还利用了 esbuild 的转译能力，将源代码转译成浏览器可执行的代码。esbuild 支持最新的 JavaScript 语法，并且具有非常快速的转译速度。这样，vite
                    可以直接在浏览器中运行转译后的代码，而无需在开发过程中进行复杂的打包操作。

                    总的来说，vite 利用 esbuild 的快速打包和转译能力，提供了一种更快速和高效的开发体验，特别适用于大型项目和复杂的前端应用程序。
                    */
                    }
                    {
                    // promise做一个专题
                    Promise.resolve().then(() => {
                    console.log(0);
                    return Promise.resolve(4);
                    }).then(res => {
                    console.log(res);
                    })

                    Promise.resolve().then(() => {
                    console.log(1);
                    }).then(() => {
                    console.log(2);
                    }).then(() => {
                    console.log(3);
                    }).then(() => {
                    console.log(5);
                    }).then(() => {
                    console.log(6);
                    })
                    }
                    {
                    // parseInt和Math.floor有什么不同
                    }
                    {
                    /*
                    <form onsubmit="event.preventDefault();">
                      <input type="submit" value="提交">
                    </form>

                    const form = document.getElementById('your-form-id');

                    form.addEventListener('submit', function(event) {
                    event.preventDefault(); // 阻止默认提交行为
                    // 在这里可以执行其他操作，如表单验证、数据处理等
                    // 如果需要手动提交表单，可以使用 form.submit() 方法
                    });

                    <form onsubmit="return false;">
                      <input type="submit" value="提交">
                    </form>
                    * /
                    }
                    {
                    /*
                    css定义media是从大到小来定义还是从小到大定义合适
                    CSS媒体查询应该从小到大来定义，而不是从大到小。这是因为媒体查询是按照从左到右的顺序进行执行的。这意味着，如果你首先使用一个媒体查询来定义某些属性，然后使用另一个媒体查询来定义其他属性，那么后面定义的属性将覆盖前面定义的属性。

                    例如，假设你正在定义一种字体的大小和粗细，你可以使用以下CSS:
                    body {
                    font-size: 16px;
                    font-weight: bold;
                    }

                    @media (max-width: 768px) {
                    body {
                    font-size: 14px;
                    font-weight: normal;
                    }
                    }

                    @media (min-width: 769px) and (max-width: 992px) {
                    body {
                    font-size: 16px;
                    font-weight: bold;
                    }
                    }


                    -----
                    @media (min-width: 768px) and (max-width: 999px) {
                    }
                    @media (max-width: 767px) {
                    }
                    上述代码中，第一个媒体查询定义了屏幕宽度大于等于 768px 且小于等于 999px 的条件，第二个媒体查询定义了屏幕宽度小于等于 767px 的条件。

                    这种方法的优点是可以更好地控制样式，因为你可以在每个媒体查询中定义不同的样式。但是，它的缺点是可能会导致样式表变得冗长和难以维护。

                    另外，还有一些其他的媒体查询方法，例如使用数字或百分比来定义屏幕尺寸，或者使用其他条件（例如设备类型、屏幕方向等）来定义媒体查询。这些方法可以根据具体需求来选择使用。
                    */
                    }
                    {
                    /*
                    offsetHeight 属性返回元素的高度，包括元素的边框、内边距和滚动条（如果存在）的高度。
                    clientHeight 属性返回元素的可见高度，不包括边框和滚动条的高度，只计算元素的内容的高度。
                    以下是使用这两个属性获取元素高度的示例代码：

                    // 使用 offsetHeight 获取元素高度
                    const element = document.getElementById('your-element-id');
                    const height = element.offsetHeight;

                    // 使用 clientHeight 获取元素高度
                    const element = document.getElementById('your-element-id');
                    const height = element.clientHeight;

                    */
                    }
                    {
                    /* kMap的键是对象，值可以是任意类型；WeakSet的值是对象，没有键名。
                    处理垃圾回收方式不同：WeakMap中的对象是弱引用，当对象被垃圾回收时，该对象的所有引用都会被移除；WeakSet中的对象也是弱引用，但只有对它的最后一个引用被移除时，该对象才会被垃圾回收。
                    遍历方式不同：WeakMap可以遍历键或值，但无法遍历键值对；WeakSet只能遍历值，无法遍历键或键值对。
                    弱引用对象不可遍历：无论是WeakMap还是WeakSet，它们的弱引用对象都是不可遍历的。
                    * /
                    }
                    {
                    /*
                    let weakmap = new WeakMap();
                    let obj = {};

                    weakmap.set(obj, 'value');
                    console.log(weakmap.get(obj)); // 'value'

                    weakmap.delete(obj); // 删除弱引用
                    console.log(weakmap.has(obj)); // false
                    */
                    }
                    {
                    /*
                    除了null还有其它方法么
                    手动解除引用：在对象生命周期结束时，手动将其引用置为null，以通知垃圾回收器回收内存。
                    弱引用：使用弱引用等“不强制要求”回收的引用类型，可以让对象在没有被引用的情况下自动被回收。
                    脱离循环引用：将循环引用改为单向引用，即A对象引用B对象，但B对象不再引用A对象，从而避免循环引用。
                    使用缓存：将对象缓存起来，避免重复创建和销毁，但要注意缓存的生命周期和使用规则，以避免循环引用问题。
                    */
                    }
                    {
                    /*
                    1意外的全局变量泄露
                    2console.log 也是全局变量
                    3闭包泄露 赋值给全局变量后，对函数的引用一直存在
                    4dom泄露，remove dom节点之后，但是remove的dom节点对子节点的引用一直存在

                    dom下新建dom，删除后引用还在，该怎么处理

                    */
                    }
                    {
                    // 跨标签页的数据共享 使用 BroadcastChannel API：这个API允许不同标签页之间进行实时通信。
                    }
                    {
                    /*
                    WebGis 中常见的优化手段
                    渲染方式: svg、canvas 2d、webgl
                    渲染策略：视窗过滤、聚合、抽稀
                    Label：聚合、碰撞检测
                    数据请求：protoBuffer、请求分页、分层绘制、切片服务
                    后端优化：视窗过滤、聚合、空间索引、抽稀vue $nextTick原理
                    */
                    }
                    {
                    // css transform 为什么是从右到左执行 codeium
                    }
                    {
                    /*

                    在JavaScript中，生成器函数是一种特殊的函数，它使用function*关键字来定义，而不是普通的函数声明或函数表达式。

                    生成器函数使用yield关键字来产生一系列值，并在每次产生值后暂停函数的执行，然后可以通过调用生成器对象的next()方法来恢复执行。
                    在生成器函数内部，我们使用yield关键字来产生值。每次调用yield语句时，函数会将当前的值返回给调用方，并暂停函数的执行。下次调用生成器对象的next()方法时，函数会从暂停的地方恢复执行，并继续生成下一个值。

                    生成器函数在处理大量数据或需要逐步生成结果的情况下非常有用。它们可以提供一种惰性计算的方式，并避免一次性生成所有结果


                    function* numberGenerator() {
                    for (let i = 1; i <= 5; i++) { yield i; } } // 使用生成器函数生成整数序列 const generator=numberGenerator(); //
                      迭代生成器对象并输出值 for (const num of generator) { console.log(num); } */ } { // vue3 watch第一个参数为什么是函数？
                      写到面试题 } { // 环境变量env的使用 整理到文档 } { // e.pre dataset 可以获取data-属性 其它属性的呢？？？ } { /* vue react 为什么要使用
                      Link 组件做页面跳转而不使用 a 标签 回去请求资源，有个空白页面闪一下 * / } { / / null和undefined到底有啥区别 /*
                      在很多编程语言中，包括JavaScript，存在两个特殊的值：null和undefined。尽管它们在某些情况下可能会被视为相似，但它们在语义上是有区别的。
                      null是一个表示“空”或“无”值的特殊值。它是一个赋给变量的值，用于指示该变量当前没有有效的对象或值。当我们想要明确地表示某个变量没有值时，可以将其赋值为null。例如，在JavaScript中，可以将一个对象属性的值设置为null来表示该属性当前没有值。
                      undefined是一个表示“未定义”或“缺少值”的特殊值。它表示变量声明但未赋值的情况，或者函数没有返回值的情况。当我们声明一个变量但没有为其赋值时，该变量的默认值为undefined。此外，在函数中未显式返回任何值的情况下，该函数的返回值也是undefined。
                      尽管null和undefined在某些情况下可能会被视为相似，但它们的使用场景略有不同。null通常用于明确表示变量没有值，而undefined则更多地用于表示缺少值或未定义的情况。
                      需要注意的是，JavaScript中的null和undefined是两个不同的值和类型。null是一个表示空值的对象类型，而undefined是一个表示未定义值的原始类型。
                      document.getElementById获取不带为啥也是null呢
                      在JavaScript中，使用document.getElementById方法来获取HTML文档中具有特定ID的元素。如果该方法找不到匹配的元素，则会返回null。
                      有几种可能的原因导致document.getElementById返回null：
                      元素不存在：如果指定的ID在HTML文档中不存在，那么document.getElementById将无法找到匹配的元素，因此返回null。
                      脚本位置：如果你的JavaScript脚本位于HTML文档中元素之前的位置，那么当脚本尝试访问元素时，可能会返回null。这是因为在JavaScript解析器执行脚本时，它尚未解析和创建位于脚本之后的HTML元素。
                      脚本执行时机：如果你的JavaScript代码在文档加载期间或DOM树构建之前执行，那么可能会尝试访问尚未存在的元素，导致document.getElementById返回null。这可以通过将脚本放置在文档末尾或使用DOMContentLoaded事件来解决，以确保脚本在文档完全加载后执行。
                      请确保在调用document.getElementById之前，HTML文档中存在具有指定ID的元素，并且脚本的位置和执行时机是正确的。如果仍然返回null，那么可能需要进一步检查HTML文档和JavaScript代码中的其他因素。
                      */ } { // 回流和重绘 } { // vue2 vue3中为什么需要key值，有什么不同 } { // 什么是bfc,哪些方法可以产生bfc /* 1.overflow:Hidden;
                      2.clear:both left right 3.clearfix::after 如果是before是否可以 * / } { / *
                      vue请求是放在created里面还是monted里面，为什么 看下codeium * / } { / / void 后面跟什么都是undefined？？？？ } { /* type class
                      ccc{} // function */ //Object.prototype[Symbol.iterator]=function () { // return
                      Object.values(this)[Symbol.iterator](); // } // let [a, b]={ a: 1, b: 2 }; // console.log(a, b);//
                      1 22 }; // console.log(a, b);// 1 2 // Object.prototype[Symbol.iterator]=function () { // return
                      Object.values(this)[Symbol.iterator](); // } // let [a, b]={ a: 1, b: 2 }; // console.log(a, b);//
                      1 22 }; // console.log(a, b);// 1 2 } { // 用正则前瞻检查密码强度 搜codeium } { // class 单例模式示例 } { //
                      flex各个参数要整理到语雀 flex: 1 1 0 %; 和flex: 1的区别 /* flex:
                      1;在浏览器中查看分别是flex-grow（设置了对应元素的增长系数）、flex-shrink(指定了对应元素的收缩规则，只有在所有元素的默认宽度之和大于容器宽度时才会触发)、flex-basis（指定了对应元素在主轴上的大小）
                      flex:1; flex-grow:1; flex-shrink:1; flex-basis:0%; */ } { /*
                      https://www.jetbrains.com/resharper/features/code_templates.html
                      https://www.jetbrains.com/resharper/features/code_templates.html $END$: 在代码片段展开后，光标停留的位置。
                      $SELECTION$: 如果在展开代码片段之前有文本被选中，则该宏将被替换为选中的文本，否则为空字符串。 $CLASS_NAME$: 当前文件的类名。 $METHOD_NAME$:
                      当前方法的名称。 $PARAMETERS$: 当前方法的参数列表。 $VARIABLE$: 当前方法的局部变量列表。 $CALLER$: 调用当前方法的方法名。 $PACKAGE_NAME$:
                      当前文件的包名。 $FILE_NAME$: 当前文件的文件名（不包含路径和后缀名）。 $DATE$: 当前日期。 $TIME$: 当前时间。 */ } { /* // 1.
                      使用offsetHeight属性获取元素的高度(不包括padding,但包括border) let
                      height=document.getElementById('id').offsetHeight; // 2.
                      使用getBoundingClientRect()方法获取元素的布局矩形,然后取高度值(不包括padding和border) let
                      height=document.getElementById('id').getBoundingClientRect().height; // 3.
                      先获取元素的style.height值,然后用parseInt()去除单位转换为数值(不包括padding) let
                      height=parseInt(document.getElementById('id').style.height); */ } { // 埋点 记录到语雀 看看语雀有没有对应的教程 } {
                      // hooka 规范 /* Hook的命名需要以use开头，比如useTimeOut，这是约定俗成的，开发者看到useXXX即可明白这是一个Hook。Hook的名称需要清楚地表明其功能。
                      只在当前关注的最顶级作用域使用Hook，而不要在嵌套函数、循环中调用Hook 函数必须是纯函数，没有副作用 返回值是一个函数或数据，供外部使用 Hook内部可以使用其他的Hook，组合功能
                      数据必须依赖于输入，不依赖于外部状态，保持数据流的明确性 在Hook内部处理错误，不要把错误抛出到外部，否则会增加hook的使用成本 Hook是单一功能的，不要给一个Hook设计过多功能。
                      作者：清欢OvO 链接：https://juejin.cn/post/7260748045309837349 来源：稀土掘金 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
                      * / } { / / vue history时 部署需要注意问题， 开发的时候有没有坑？？ } { /* 为什么等于null就释放资源了呢 在 JavaScript 中，null
                      是一个表示空值的特殊值。当你将一个变量设置为 null 时，实际上是告诉 JavaScript 运行时，该变量不再引用任何对象或值。 当一个对象或值不再被引用时，JavaScript
                      的垃圾回收机制会自动回收这些没有被引用的对象或值所占用的内存空间。这是为了释放不再使用的资源，以便其他部分的代码可以使用这些资源。 因此，当你将一个变量设置为 null
                      时，相应的对象或值会失去引用，如果没有其他地方引用该对象或值，垃圾回收机制将会在适当的时机回收相关的资源。 需要注意的是，手动将一个变量设置为 null
                      并不总是释放相关资源的必要条件。JavaScript 的垃圾回收机制会自动管理资源的释放，你只需要确保没有任何引用指向该资源即可。 */ } { // 二叉树中的后序遍历？ } { // scss
                      都有哪些 for循环 @include dengdeng } { // 使用迭代器比较版本号可以通过使用自定义的迭代器函数来实现 function
                      compareVersions(version1, version2) { const iterator1=versionIterator(version1); const
                      iterator2=versionIterator(version2); let part1=iterator1.next().value; let
                      part2=iterator2.next().value; while (part1 !==undefined || part2 !==undefined) { const
                      num1=parseInt(part1) || 0; const num2=parseInt(part2) || 0; if (num1 < num2) { return -1; } else
                      if (num1> num2) {
                      return 1;
                      }

                      part1 = iterator1.next().value;
                      part2 = iterator2.next().value;
                      }

                      return 0;
                      }

                      function* versionIterator(version) {
                      const parts = version.split('.');
                      for (const part of parts) {
                      yield part;
                      }
                      }

                      // 示例用法
                      const version1 = '1.2.3';
                      const version2 = '1.2.4';
                      const result = compareVersions(version1, version2);
                      console.log(result); // 输出：-1

                      }
                      {
                      // vue 虚拟列表模拟一个 vue2 vue3 react的
                      }
                      {
                      // 树和树的遍历
                      }
                      {
                      // 数组去重有多少种方式
                      }
                      {
                      // 闭包真的闭吗
                      var InstanceObj = (function () {
                      var obj = {
                      a: 1,
                      b: 2
                      }
                      Object.setPrototypeOf(obj, null);
                      return {
                      get: function (k) {
                      if (obj.hasOwnProperty(k)) {
                      return obj[k];
                      }
                      return undefined;
                      // return obj[k];
                      }
                      }
                      })();

                      Object.prototype._hasOwnProperty = Object.prototype.hasOwnProperty;
                      Object.defineProperty(Object.prototype, 'hasOwnProperty', {
                      value: function (k) {
                      InstanceObj.obj = this;
                      return Object.prototype._hasOwnProperty.call(this, k);
                      }
                      })
                      InstanceObj.get('a');
                      InstanceObj.obj.a = 4;
                      console.log(InstanceObj.get('a'));

                      Object.defineProperty(Object.prototype, 'abc', {
                      get() {
                      return this;
                      }
                      })
                      var obj2 = InstanceObj.get('abc');
                      obj2.c = 666;
                      obj2.a = 'boxue';
                      console.log(InstanceObj.get('a'));
                      }
                      {
                      // Javascript 中的数据结构 | 线性搜索和二进制搜索

                      }
                      {
                      // 在TypeScript 中如何正确使用interface 和type
                      }
                      {
                      // eslint的配置文件拷贝一份出来，学习下各个参数
                      }
                      {
                      // Number属性看一遍
                      }
                      {
                      // 函数柯力化
                      }
                      {
                      // vue的运行时编译和模板预编译
                      }
                      {
                      // webpack的编译过程
                      }
                      {
                      // vue 响应式整体流程
                      }
                      {
                      // js中获取dom元素尺寸的方法
                      }
                      {
                      // console 有哪些方法，整理到文档
                      }
                      {
                      // 内存泄漏 dom泄漏 在浏览琴控制台咋么看？
                      }
                      {
                      // 新建一个操作符面试题文档
                      let a = 10;
                      let x = a-- + ++a;
                      console.log(x, a);
                      }
                      {
                      // passive:它可以用来告诉浏览器该事件监听器不会调用preventDefault()方法来阻止默认行为，从而提高滚动性能。
                      element.addEventListener('wheel', handleWheel, { passive: true });

                      function handleWheel(event) {
                      // 执行滚动相关的操作
                      }
                      }
                      {
                      // 类数组转换成数组 codeium 推荐Array.prototype.
                      }
                      {
                      var arr = [0];
                      if (arr) {
                      console.log(arr == true);// false
                      } else {
                      console.log('error');
                      }
                      // let arr = [1, 2, 3];
                      // arr.valueOf(); // 返回数组本身 [1, 2, 3]
                      // arr.toString(); // 返回字符串 '1,2,3'
                      // Number(arr); // 将'1,2,3'转换为数字,结果为0
                      }
                      {
                      // 函数声明会优先于函数表达式,在声明和表达式具有相同名称的情况下，函数声明将会覆盖函数表达式。
                      var getName = () => { console.log(1) }
                      function getName() {
                      console.log(2)
                      }
                      getName();// 1
                      }
                      {
                      var t1 = new Function('console.log("t2")');
                      var t2 = Function('console.log("t2")');
                      console.log(t1.__proto__ === Function.prototype);
                      console.log(Function.__proto__ === Function.prototype);

                      var code = `function test(){console.log("test")}`;
                      eval(`!${code}()`);
                      }
                      {
                      // 广度优先遍历和深度优先遍历 搜下coceium
                      }
                      {
                      /*
                      JavaScript 中有多种隐式类型转换规则，其中一些常见的规则如下：

                      字符串与数字之间的转换：当字符串与数字进行操作时，JavaScript 会尝试将其中一个操作数转换为另一个类型。例如，使用 +
                      运算符时，如果其中一个操作数是字符串，那么另一个操作数也会被转换为字符串，然后执行字符串拼接操作。
                      布尔值与其他类型之间的转换：布尔值在需要转换为其他类型时会被隐式转换。例如，使用逻辑运算符（如 &&、||）时，布尔值会被转换为其他类型的值。
                      数字与布尔值之间的转换：在需要将数字转换为布尔值时，JavaScript 使用以下规则：0、NaN、null、undefined 和空字符串 ("") 被视为 false，其他数字被视为
                      true。
                      对象与原始值之间的转换：当对象与原始值进行操作时，JavaScript 会尝试将对象转换为原始值。这个过程涉及到对象的 valueOf() 和 toString() 方法的调用。
                      需要注意的是，隐式类型转换可能会导致一些意外的结果和错误。为了避免这种情况，建议在代码中显式地进行类型转换，以增加代码的可读性和可维护性。可以使用
                      Number()、String()、Boolean() 等函数来显式地进行类型转换。

                      "0.00" == 0的原理
                      在这个例子中，字符串 "0.00" 和数字 0 是不同的类型。当进行比较时，JavaScript 会尝试将字符串转换为数字类型。
                      根据 JavaScript 的规则，如果字符串可以被解析为一个有效的数字，它将被转换为相应的数字类型。而字符串 "0.00" 可以被解析为数字 0。
                      因此，表达式 "0.00" == 0 的结果为 true。
                      需要注意的是，这种类型转换可能会导致一些意外的结果，因此在进行比较时应谨慎。如果你希望进行严格的比较，包括类型和值的比较，可以使用 === 运算符，即 "0.00" ===
                      0，这样两个不同类型的值将不会被转换。
                      */
                      }
                      {
                      var a = 1, b = 2;
                      function test() {
                      var b = 3;
                      // eval('!function _(c){console.log(a+b+c)(4)}');// 8 创建了闭包
                      // return function(){} //也是创建了闭包
                      return new Function('c', 'return a + b + c');
                      }
                      var t = test();
                      t(4);// 浏览器环境:7 node环境:a is node defined
                      }
                      {
                      var { x = 3 } = { x: undefined };//x=3
                      var { y = 3 } = { y: null };//y=null,null不严格等于undefined
                      }
                      {
                      console.log(({} + {}).length);// 30 类型转换 '[object Object][object Object]'
                      console.log(([] + []).length);// 0 []会被转换成空字符串

                      console.log((function () { }).length);// 0
                      console.log((function (a, b) { }).length);// 2 形参个数

                      function test(a, b, c) {
                      console.log(arguments.length);// 4 实参个数
                      }
                      test(1, 2, 3, 4);
                      console.log(test.length);// 3 函数的形参
                      }
                      {
                      // js跨域的几种方法，代码整理一下
                      }
                      {
                      /* isPrototypeOf 是 JavaScript 中的一个方法，用于检查一个对象是否是另一个对象的原型。它用于确定对象之间的原型链关系。
                      请注意，isPrototypeOf 方法是在原型链上进行检查的，所以只有在原型链中的对象才会返回 true。如果要检查一个对象是否继承自某个特定的构造函数，可以使用 instanceof 运算符。
                      */
                      const parentObj = { name: "Alice" };
                      const childObj = Object.create(parentObj);

                      console.log(parentObj.isPrototypeOf(childObj)); // 输出: true
                      console.log(childObj.isPrototypeOf(parentObj)); // 输出: false

                      }
                      {
                      /*
                      isPrototypeOf 方法并不用于对象属性的校验，而是用于检查一个对象是否是另一个对象的原型。它在判断对象之间的原型继承关系时非常有用。

                      如果你想要对对象的属性进行校验，可以使用其他方法，比如 hasOwnProperty、in 运算符或者使用第三方库如 Lodash 或 Joi。

                      hasOwnProperty 用于检查对象是否有指定的自有属性，in 运算符用于检查对象是否有指定的属性，Lodash 的 has 方法提供了更丰富的功能，而 Joi
                      是一个流行的数据校验库，可以进行更复杂的属性校验。

                      以下是一些示例：
                      */
                      const obj = { name: "Alice", age: 25 };

                      // 使用 hasOwnProperty 方法检查对象是否有指定属性
                      console.log(obj.hasOwnProperty("name")); // 输出: true
                      console.log(obj.hasOwnProperty("address")); // 输出: false

                      // 使用 in 运算符检查对象是否有指定属性
                      console.log("name" in obj); // 输出: true
                      console.log("address" in obj); // 输出: false

                      // 使用 Lodash 库的 has 方法检查对象是否有指定属性
                      const _ = require("lodash");
                      console.log(_.has(obj, "name")); // 输出: true
                      console.log(_.has(obj, "address")); // 输出: false

                      // 使用 Joi 库进行更复杂的属性校验
                      const Joi = require("joi");
                      const schema = Joi.object({
                      name: Joi.string().required(),
                      age: Joi.number().integer().min(18)
                      });

                      const validationResult = schema.validate(obj);
                      console.log(validationResult.error); // 输出: null，表示校验通过

                      }
                      {
                      let [a = true] = [null];// a:null
                      let [b = true] = [undefined];// b:true
                      let [c = true] = [1];// c:1
                      let [d = false] = [undefined];// d:false
                      let [e = false] = [null];// e:null
                      let [f = 0] = [true];// f:true
                      let [g = 0] = [false];// g:false
                      }
                      {
                      //js 宏任务和微任务 请求，定时器，事件
                      // 什么叫事件循环
                      }
                      {
                      // js打印对象原型链上的函数的主体内容
                      function printPrototypeFunctions(obj) {
                      let proto = Object.getPrototypeOf(obj);

                      while (proto !== null) {
                      Object.getOwnPropertyNames(proto).forEach((name) => {
                      let property = Object.getOwnPropertyDescriptor(proto, name);
                      if (typeof property.value === 'function') {
                      console.log(name + ': ' + property.value.toString());
                      }
                      });

                      proto = Object.getPrototypeOf(proto);
                      }
                      }

                      // 例子
                      let obj = {}; // 替换为你的对象
                      printPrototypeFunctions(obj);
                      }
                      {
                      function curry(fn) {
                      return function curried(...args) {
                      if (args.length >= fn.length) {
                      return fn.apply(this, args);
                      } else {
                      return function (...moreArgs) {
                      return curried.apply(this, args.concat(moreArgs));
                      }
                      }
                      }
                      }
                      function add(a, b, c) {
                      return a + b + c;
                      }

                      const curriedAdd = curry(add);

                      console.log(curriedAdd(1)(2)(3)); // 输出 6
                      console.log(curriedAdd(1, 2)(3)); // 输出 6
                      console.log(curriedAdd(1)(2, 3)); // 输出 6

                      }
                      {
                      // 手写Promise
                      class MyPromise {
                      constructor(executor) {
                      this.state = 'pending';
                      this.value = undefined;
                      this.onResolveCallbacks = [];
                      this.onRejectCallbacks = [];

                      const resolve = (value) => {
                      if (this.state === 'pending') {
                      this.state = 'fulfilled';
                      this.value = value;
                      this.onResolveCallbacks.forEach(callback => callback(value));
                      }
                      };

                      const reject = (reason) => {
                      if (this.state === 'pending') {
                      this.state = 'rejected';
                      this.value = reason;
                      this.onRejectCallbacks.forEach(callback => callback(reason));
                      }
                      };

                      try {
                      executor(resolve, reject);
                      } catch (error) {
                      reject(error);
                      }
                      }

                      then(onResolve, onReject) {
                      return new MyPromise((resolve, reject) => {
                      if (this.state === 'fulfilled') {
                      try {
                      const result = onResolve(this.value);
                      resolve(result);
                      } catch (error) {
                      reject(error);
                      }
                      } else if (this.state === 'rejected') {
                      try {
                      const result = onReject(this.value);
                      resolve(result);
                      } catch (error) {
                      reject(error);
                      }
                      } else {
                      this.onResolveCallbacks.push((value) => {
                      try {
                      const result = onResolve(value);
                      resolve(result);
                      } catch (error) {
                      reject(error);
                      }
                      });

                      this.onRejectCallbacks.push((reason) => {
                      try {
                      const result = onReject(reason);
                      resolve(result);
                      } catch (error) {
                      reject(error);
                      }
                      });
                      }
                      });
                      }

                      catch(onReject) {
                      return this.then(null, onReject);
                      }
                      }

                      }
                      {
                      // 对象的键为string或Symbol
                      var a = {};
                      var b = { key: 'b' };
                      var c = { key: 'c' };
                      // b.toString() = '[object Object]'
                      a[b] = 123;// a['object Object'] = 123
                      a[c] = 456;// a['object Object'] = 456
                      console.log(a[b]);// 456 a['object Object']
                      }
                      {
                      let arr = [];
                      arr[1] = 2;
                      arr['1'] = 3;
                      console.log(arr[1]);// 3
                      console.log(arr['1']);// 3
                      }
                      {
                      var a = {};
                      var b = {
                      key: 'a'
                      };
                      var c = {
                      key: 'c'
                      }
                      a[b] = '123';
                      a[c] = '456';
                      console.log(a[b]);// '456'
                      console.log(a[c]);// '456'
                      console.log(a);// {[object Object]: "456"}
                      }
                      {
                      var a = 5;
                      var b = 6;
                      // 如何不借助第三个变量完成上面的交换

                      // 第一种
                      {
                      a = a + b;
                      b = a - b;
                      a = a - b;
                      }
                      // 第二种(只能数字？？？)
                      {
                      a = a ^ b;
                      b = a ^ b;
                      a = a ^ b;
                      }
                      // 第三种
                      {
                      [b, a] = [a, b]
                      }
                      }
                      {
                      /*
                      Symbol 类型的键在使用 Object.keys()、Object.entries() 和 JSON.stringify() 方法时会被忽略。
                      */
                      const isEmptyObject_0 = (obj) => Reflect.ownKeys(obj).length === 0;
                      const isEmptyObject_1 = (obj) => Object.keys(obj).length === 0;
                      const isEmptyObject_2 = (obj) => Object.entries(obj).length === 0;
                      const isEmptyObject_3 = (obj) => Object.getOwnPropertyNames(obj).length === 0;
                      const isEmptyObject_4 = (obj) => JSON.stringify(obj) === '{}';
                      const isEmptyObject_5 = (obj) => JSON.stringify(obj) === '{}';
                      function isEmptyObject_6(obj) {
                      for (let key in obj) {
                      if (obj.hasOwnProperty(key)) {
                      return false;
                      }
                      }
                      return true;
                      }
                      function isEmptyObject_7(obj) {
                      let flag = true;
                      for (let key in obj) {
                      if (key) {
                      flag = false;
                      break;
                      }
                      }
                      return flag;
                      }

                      const emptyObj = {};
                      const nonEmptyObj = { name: 'John', age: 25 };
                      const objWithSymbol = { [Symbol('key')]: 'value' };

                      console.log(isEmptyObject_0(emptyObj), isEmptyObject_0(nonEmptyObj),
                      isEmptyObject_0(objWithSymbol)); // 输出：true false false
                      console.log(isEmptyObject_1(emptyObj), isEmptyObject_1(nonEmptyObj),
                      isEmptyObject_1(objWithSymbol)); // 输出：true false true
                      console.log(isEmptyObject_2(emptyObj), isEmptyObject_2(nonEmptyObj),
                      isEmptyObject_2(objWithSymbol)); // 输出：true false true
                      console.log(isEmptyObject_3(emptyObj), isEmptyObject_3(nonEmptyObj),
                      isEmptyObject_3(objWithSymbol)); // 输出：true false true
                      console.log(isEmptyObject_4(emptyObj), isEmptyObject_4(nonEmptyObj),
                      isEmptyObject_4(objWithSymbol)); // 输出：true false true
                      console.log(isEmptyObject_5(emptyObj), isEmptyObject_5(nonEmptyObj),
                      isEmptyObject_5(objWithSymbol)); // 输出：true false true
                      console.log(isEmptyObject_6(emptyObj), isEmptyObject_6(nonEmptyObj),
                      isEmptyObject_6(objWithSymbol)); // 输出：true false true
                      console.log(isEmptyObject_7(emptyObj), isEmptyObject_7(nonEmptyObj),
                      isEmptyObject_7(objWithSymbol)); // 输出：true false true
                      // 下面是jq里面的方法['1.11.3','3.6.4'] 返回结果都是true
                      // const objWithSymbol = { [Symbol('key')]: 'value' };
                      // console.log($.isEmptyObject(objWithSymbol));// true
                      }
                      {
                      function A() { }
                      function B() { }
                      function create() {
                      if (Math.random() > 0.5) {
                      return new A();
                      } else {
                      return new B();
                      }
                      }
                      let obj = create();
                      // 下面当Math.random()<0.5时 console.log(obj instanceof A);// false console.log(obj instanceof B);//
                        true // 下面获取执行函数的名称 console.log(obj.__proto__.constructor.name);// B } { /* vue
                        mixin的使用，查看下其它的用法记录到文档 < template>
                        <div>
                          <button @click="increment">{{ count }}</button>
                        </div>
                  </template>

                  <script>
              const CounterMixin = {
                data() {
          return {
                count: 0,
          };
        },
              methods: {
                increment() {
                this.count++;
          },
        },
      };

              export default {
                mixins: [CounterMixin], // 引入 mixin 对象
        // 组件选项
      };
                  </script>
                  */
                  }

                  {
                  /*
                  type和interface的区别
                  */
                  }
                  {
                  /* 重排和重绘的区别
                  重排（reflow）是指当页面的布局和几何属性发生改变时，浏览器会重新计算元素的大小和位置，然后重新构建页面的布局树和绘制树。重排是一个相对较慢的操作，因为它会涉及到重新计算多个元素的几何属性，并且可能会引发其他元素的重排。

                  一些常见的导致重排的操作包括：

                  修改元素的尺寸（宽度、高度）
                  修改元素的位置（top、left）
                  修改元素的边距（margin）
                  修改元素的字体大小
                  修改元素的样式（颜色、背景等）
                  重绘（repaint）是指当元素的外观或样式发生改变时，浏览器会重新绘制元素的可视部分。重绘不会影响页面的布局，只会更新元素的外观。相对于重排，重绘的开销较小。

                  一些常见的导致重绘的操作包括：

                  修改元素的背景颜色
                  修改元素的文本颜色
                  修改元素的边框样式
                  为了提高性能，减少页面的重排和重绘，可以采取以下措施：

                  使用 CSS3 动画和过渡，它们通常比 JavaScript 动画性能更好，因为它们在浏览器的合成线程上运行，而不会引发重排和重绘。
                  将需要多次修改的样式属性合并为一个操作，以减少重排和重绘的次数。
                  使用 transform 属性来进行元素的平移、旋转和缩放，它可以在不引发重排的情况下修改元素的外观。
                  尽量避免在布局树中的每个节点上使用复杂的选择器，这可能导致浏览器重新计算样式，引发重排。
                  总结：

                  重排是浏览器重新计算元素的布局和几何属性，而重绘是重新绘制元素的外观。
                  重排比重绘开销更大，因为它会涉及到重新计算多个元素的属性和布局。
                  为了提高性能，应该尽量减少页面的重排和重绘次数。
                  */
                  }
                  {
                  /* ts symbol类型 */
                  }
                  {
                  /*
                  call和apply的区别

                  */
                  }
                  {
                  /*
                  vue history模式下部署服务器要注意什么问题

                  */
                  }
                  {
                  /*
                  vue3 defineAsyncComponent 的使用示例
                  import { defineAsyncComponent } from 'vue';

                  const AsyncComponent = defineAsyncComponent(() =>
                  import('./components/AsyncComponent.vue')
                  );

                  export default {
                  components: {
                  AsyncComponent,
                  },
                  };



                  <template>
                    <div>
                      <h1>Parent Component</h1>
                      <AsyncComponent />
                    </div>
                  </template>

                  */

                  }
                  {
                  /*
                  js实现一个方法，失败重试

                  */
                  async function retryOperation(operation, maxAttempts, delay) {
                  let attempts = 0;
                  while (attempts < maxAttempts) { try { // 执行操作 const result=await operation(); return result; } catch
                    (error) { console.error(`Attempt ${attempts + 1} failed:`, error); attempts++; await new
                    Promise(resolve=>
                    setTimeout(resolve, delay)); // 延迟一段时间后重试
                    }
                    }
                    throw new Error(`Operation failed after attempts.`);
                    }

                    }
                    {
                    /*
                    vue3 中tree shaking
                    在 Vue 3 中，Tree Shaking 是一种优化技术，用于从最终的构建包中删除未使用的代码，以减小包的大小。它可以通过静态分析来检测和删除未使用的模块、函数、变量等，从而减少最终的项目体积。

                    Vue 3 的 Tree Shaking 遵循标准的 ES 模块语法和工具链的支持。在使用 Vue 3 进行开发时，你可以利用以下几种方式来优化 Tree Shaking：

                    使用 ES 模块语法：在编写代码时，尽可能使用 ES 模块语法（import 和 export）来导入和导出模块。这样可以帮助工具链更好地进行静态分析和优化。

                    使用按需引入：Vue 3 的核心库（@vue/runtime-core）提供了按需引入的功能，可以根据需要只引入所使用的功能模块。这样可以避免引入未使用的模块，减小最终构建包的大小。

                    例如，可以使用 import { createApp, ref } from 'vue' 来只引入 createApp 和 ref，而不引入其他未使用的功能模块。

                    避免副作用：在编写代码时，尽量避免在模块中执行副作用操作，如直接修改全局变量、注册全局组件等。这样可以帮助工具链更好地进行静态分析和优化。

                    配置工具链：如果你使用的是现代的打包工具（如 webpack），可以通过配置工具链来启用 Tree Shaking。确保在构建配置中启用了相应的 Tree Shaking 插件和配置选项。

                    需要注意的是，Tree Shaking 的效果取决于工具链的支持和配置，以及代码的编写方式。在实际开发中，你可以使用工具链的构建分析工具来检查最终构建包的大小和组成，以确保 Tree Shaking
                    的有效性。

                    */
                    }
                    {
                    /*
                    vue3中dom diff过程
                    在 Vue 3 中，DOM Diff 过程有以下几个步骤：

                    生成新的 VNode：根据组件模板或 render 函数生成新的 VNode 树。

                    比较新旧 VNode：Vue 3 使用了一种称为 PatchFlag 的新的标记系统来跟踪 VNode 的变化。在比较新旧 VNode 时，Vue 3 会根据 PatchFlag 来判断 VNode
                    是否具有可比较的属性。

                    按需更新：根据比较结果，Vue 3 会对需要更新的部分进行按需更新，而不是全量更新整个组件。

                    如果新旧 VNode 完全相同，则不会进行任何更新。
                    如果新旧 VNode 不相同，但具有相同的父节点，则会进行更新操作，包括属性的更新、文本内容的更新等。
                    如果新旧 VNode 不相同且没有相同的父节点，则会进行替换操作，将旧的 VNode 替换为新的 VNode。
                    如果新旧 VNode 不相同，但具有相同的标签名，则会对子节点进行逐个比较和更新，而不会替换整个子节点。
                    更新子节点：在更新子节点时，Vue 3 使用了一种称为 Keyed Diff 的算法来优化对子节点的比较和更新。Keyed Diff 算法会根据节点的 key 属性来进行比较和更新，以减少更新的操作。
                    如果旧的子节点和新的子节点都具有 key 属性，则 Vue 3 会根据 key 属性进行比较和更新。
                    如果旧的子节点或新的子节点没有 key 属性，则会使用一种称为 Iterating Sequence Algorithm 的算法来进行比较和更新。
                    完成更新：最后，Vue 3 会完成所有需要更新的操作，并将更新后的 VNode 渲染到真实的 DOM 中。
                    Vue 3 的 DOM Diff 过程相对于 Vue 2 有一些改进，包括使用 PatchFlag 进行标记和按需更新的优化。这些改进使得 Vue 3 在性能和效率上有所提升。

                    */
                    }
                    {
                    /*
                    vue2中路由跳转有哪些方式，怎么接收参数以及优缺点

                    */
                    }
                    {
                    /*
                    mixin作用和场景以及缺点

                    Mixin 是一种在 Vue 中重用组件逻辑的方式。它允许将一组选项（如数据、计算属性、方法等）混入到多个组件中，以实现代码的复用。Mixin 可以用于以下场景：

                    作用：

                    代码复用：Mixin 可以将一组逻辑和功能集中到一个地方，并在多个组件中重复使用。这可以减少代码的冗余，提高开发效率。
                    功能扩展：Mixin 可以用于扩展组件的功能。通过混入一些常用的方法或计算属性，可以为多个组件提供共享的功能。
                    逻辑抽象：Mixin 可以将一些通用的逻辑抽象出来，提供给多个组件使用。这可以使组件更加简洁和可读。
                    场景：

                    共享逻辑：当多个组件需要共享相同的逻辑时，可以使用 Mixin 将这些逻辑提取出来，并在多个组件中混入。
                    通用功能：当有一些通用的功能需要在多个组件中使用时，可以使用 Mixin 将这些功能封装为一个 Mixin，并在需要的组件中混入。
                    代码复用：当多个组件之间存在一些重复的代码时，可以使用 Mixin 将这些代码提取出来，并在多个组件中复用。
                    缺点： 尽管 Mixin 提供了一种方便的代码复用机制，但过度使用 Mixin 可能会导致一些问题：

                    命名冲突：当多个 Mixin 中具有相同的属性或方法时，可能会导致命名冲突，使代码难以理解和维护。
                    依赖关系不清晰：当一个组件使用了多个 Mixin 时，组件的逻辑和依赖关系可能会变得复杂，导致代码难以理解和调试。
                    组件耦合度增加：过多的 Mixin 可能使组件之间的耦合度增加，使代码的维护和扩展变得困难。
                    因此，在使用 Mixin 时需要注意适度使用，确保代码的可读性、可维护性和可扩展性

                    */
                    }
                    {
                    /*
                    对象的呢
                    在 Vue 2 中，当操作对象时，一些对象的属性操作会触发视图的更新，而另一些则不会触发。下面是一些常见的对象属性操作的情况：

                    会触发更新的对象属性操作：

                    直接通过赋值语句修改对象的属性值，例如：obj.property = value。
                    使用 Vue.set 或 vm.$set 方法向对象添加新的属性，并给属性赋值，例如：Vue.set(obj, 'newProperty', value) 或 vm.$set(obj,
                    'newProperty',
                    value)。
                    使用 Object.assign 或 spread 运算符创建一个新的对象，并赋值给原始对象的引用，例如：obj = Object.assign({}, obj, { newProperty:
                    value }) 或 obj
                    = { ...obj, newProperty: value }。
                    这些对象属性操作会修改对象的属性值或对象的引用，从而触发 Vue 进行重新渲染。

                    不会触发更新的对象属性操作：

                    直接通过赋值语句修改对象的属性值，但属性值与之前相同，例如：obj.property = obj.property。
                    使用 delete 关键字删除对象的属性，例如：delete obj.property。
                    使用 Object.freeze 方法冻结对象，使其属性不可修改。
                    这些对象属性操作不会修改对象的属性值或对象的引用，因此不会触发 Vue 进行重新渲染。

                    需要注意的是，Vue 2 中的响应式系统是基于 JavaScript 对象的 getter 和 setter 实现的，当通过 getter 获取对象的属性时，Vue
                    会追踪这个属性的依赖关系，并在属性发生变化时触发更新
                    */
                    }
                    {
                    /*
                    vue2 数组的哪些属性操作会更新，哪些不会
                    在 Vue 2 中，当操作数组时，一些数组的属性操作会触发视图的更新，而另一些则不会触发。下面是一些常见的数组属性操作的情况：

                    会触发更新的数组属性操作：

                    push(): 向数组末尾添加元素。
                    pop(): 从数组末尾移除元素。
                    shift(): 从数组开头移除元素。
                    unshift(): 向数组开头添加元素。
                    splice(): 在指定位置插入、删除或替换元素。
                    sort(): 对数组进行排序。
                    reverse(): 反转数组的顺序。
                    这些数组属性操作会修改原始数组，从而触发 Vue 进行重新渲染。

                    不会触发更新的数组属性操作：

                    slice(): 返回一个新的数组，不会修改原始数组。
                    concat(): 返回一个新的数组，将原始数组与其他数组或值合并，但不会修改原始数组。
                    filter(): 返回一个新的数组，根据指定的条件筛选原始数组中的元素，但不会修改原始数组。
                    map(): 返回一个新的数组，根据指定的逻辑对原始数组中的元素进行转换，但不会修改原始数组。
                    这些数组属性操作会返回一个新的数组，而不是直接修改原始数组，因此不会触发 Vue 进行重新渲染。

                    需要注意的是，如果你使用 vm.$set 或 Vue.set 方法来对数组进行修改，Vue 会将其视为修改了数组，并触发更新。例如，vm.$set(array, index, value)
                    可以用于在数组中指定的索引位置设置一个新值，并触发视图更新。

                    */
                    }
                    {
                    /*

                    vue2 domdiff过程

                    在 Vue 2 中，DOM Diff 是用于计算虚拟 DOM 树之间的差异，并将差异应用到实际 DOM 上的过程。下面是 Vue 2 中 DOM Diff 的大致过程：

                    创建新的虚拟 DOM 树：在每次组件更新时，Vue 会先创建一个新的虚拟 DOM 树，表示组件的最新状态。

                    比较新旧虚拟 DOM 树：Vue 会将新的虚拟 DOM 树与上一次更新时的旧虚拟 DOM 树进行比较，找出两者之间的差异。

                    计算差异：在比较新旧虚拟 DOM 树时，Vue 会遍历两棵树的节点，并进行差异计算。Vue 使用了一种高效的算法叫做 Virtual DOM
                    Diffing，该算法会尽量在最小的操作次数内找出最小的差异。

                    生成差异补丁：在计算差异后，Vue 会生成一系列的差异补丁（patch）。每个差异补丁描述了需要对实际 DOM 进行的一项修改操作，如插入、更新或删除节点等。

                    应用差异补丁：Vue 会将生成的差异补丁应用到实际的 DOM 上，从而实现视图的更新。Vue 使用了一种叫做 Patch 的机制，根据差异补丁的描述，按照一定的顺序对实际 DOM 进行修改。

                    通过这个过程，Vue 2 可以高效地跟踪组件的状态变化，并将最小的修改应用到实际 DOM 上，从而实现高性能的视图更新。

                    需要注意的是，Vue 2 中的 DOM Diff 是基于 Virtual DOM 的，而不是直接操作实际 DOM。Vue 2 通过比较和计算虚拟 DOM 树的差异，最终只会对实际 DOM
                    进行最小的操作，从而提高性能和效率。
                    */
                    }
                    {
                    /*
                    props attr listeners 的作用和场景
                    props：props 是用于从父组件向子组件传递数据的属性。父组件可以通过在子组件上定义 props 来声明要传递的数据，并通过属性的方式将数据传递给子组件。子组件可以使用这些 props
                    来接收并使用父组件传递的数据。这种方式适用于父子组件之间的单向数据流通信。

                    attrs：attrs 是用于将父组件上非 props 属性传递给子组件的属性。当父组件将属性传递给子组件时，如果子组件没有声明对应的 props，那么这些属性会被自动添加到子组件的 attrs
                    属性中。子组件可以通过
                    $attrs 访问这些属性。这种方式适用于需要将一些额外的属性传递给子组件的场景。

                    listeners：listeners 是用于将父组件上的事件监听器传递给子组件的属性。当父组件在子组件上使用 v-on 或 @ 来监听事件时，这些监听器会被自动添加到子组件的 listeners
                    属性中。子组件可以通过
                    $listeners 访问这些事件监听器，并使用 $emit 来触发父组件上的事件。这种方式适用于子组件需要触发父组件中定义的事件的场景。

                    这些属性在组件之间进行通信时非常有用，可以实现父子组件之间的数据传递和事件触发。它们的作用和使用场景如上所述。


                    listeners的使用示例 找一个示例
                    Event Bus的使用
                    */
                    }
                    {
                    /*
                    vue2中组件通信的方式有哪些

                    Props / $emit：通过 props 属性将数据从父组件传递给子组件，子组件可以通过 $emit 方法触发自定义事件来向父组件传递数据。这是最基本和常用的组件通信方式。

                    Vuex：Vuex 是 Vue 的官方状态管理库，可以用于在不同组件之间共享和管理状态。通过在组件中派发 commit 或 dispatch 方法来修改或获取状态，组件可以实现跨层级的通信。

                    $ref：通过 $ref 可以在父组件中引用子组件实例，并直接访问子组件的属性和方法。这种方式可以实现父组件主动调用子组件的操作。

                    Event Bus：使用一个空的 Vue 实例作为事件总线，通过 $on 和 $emit 方法来进行组件之间的通信。任何一个组件都可以订阅事件或触发事件，从而实现组件之间的解耦。

                    Provide / Inject：通过 provide 和 inject 可以在父组件中提供数据，在后代组件中注入并使用这些数据。这种方式可以实现跨层级的组件通信。

                    $parent / $children：通过 $parent 和 $children 可以访问父组件和子组件的实例，从而实现组件之间的直接通信。但是这种方式不够灵活，并且破坏了组件的封装性。

                    每种通信方式都有其适用的场景和注意事项。需要根据具体的需求选择合适的方式来实现组件之间的通信。
                    */
                    }

                    {
                    /* nextTick原理
                    nextTick 是 Vue 中一个常用的方法，它用于在下次 DOM
                    更新循环结束之后执行延迟回调函数。它的原理是利用浏览器的事件循环机制，在当前代码执行完毕后，利用微任务或宏任务的方式来异步执行回调函数。

                    具体来说，nextTick 的实现原理如下：

                    首先，Vue 会先判断浏览器是否支持原生的 Promise 对象，如果支持，就使用 Promise 来创建一个微任务。

                    如果浏览器不支持 Promise，Vue 会判断浏览器是否支持原生的 MutationObserver，如果支持，就使用 MutationObserver 来创建一个微任务。

                    如果浏览器既不支持 Promise 也不支持 MutationObserver，Vue 会退回到使用 setTimeout 创建一个宏任务。

                    无论使用哪种方式，nextTick 的原理都是将传入的回调函数放入一个任务队列中，并在下次事件循环中执行该任务队列。

                    下面是一个示例代码，演示了如何使用 nextTick：
                    */
                    // 在 Vue 组件中使用 nextTick
                    this.$nextTick(() => {
                    // 在 DOM 更新循环结束后执行的回调函数
                    // 可以在这里操作已更新的 DOM
                    });

                    // 在普通 JavaScript 中使用 nextTick
                    Vue.nextTick(() => {
                    // 在 DOM 更新循环结束后执行的回调函数
                    // 可以在这里操作已更新的 DOM
                    });
                    /*
                    需要注意的是，nextTick 并不是立即执行回调函数，而是在下次事件循环中执行。这意味着在调用 nextTick 后，不能立即获取到更新后的 DOM。如果需要在 DOM
                    更新后执行一些操作，可以将这些操作放在
                    nextTick 的回调函数中。
                    */
                    }


                    </script>
</body>

</html>